---
sidebar_position: 6
---

import { StoryEmbed } from '../../src/components/StoryEmbed';

# Search Functionality

Searching for items is natively supported. It looks through all items that are curretly visible in the
tree, similar to how other tree implementations do it such as the tree view in IntelliJ or VsCode. Search
is automatically started if the tree is focused and the user starts typing.

This also implements the accessibility feature where entering a single character while focusing the tree
should move the focus to the first item that matches the character. See the
[W3C spec for keyboard bindings](https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2a.html#kbd_label)
for more details.

<StoryEmbed storyName="auto-demo-autodemo-component--search-demo" iframeProps={{ width: 600 }} />

:::info
All props that can be provided to the tree environment to control search capabilities are documented
in the [TreeCapabilities interface](/docs/api/interfaces/TreeCapabilities).
:::

:::warning
(TODO) Apparently the search currently does not work properly in the docs pages, where the search input
is not properly shown. This is an issue with our documentation tooling, not the framework.
Working examples are available in the storybook.
:::

## Example

Try focusing the tree (i.e. by clicking on it) and start typing to search.

```jsx live
function App() {
  return (
    <UncontrolledTreeEnvironment
      dataProvider={new StaticTreeDataProvider(longTree.items, (item, data) => ({ ...item, data }))}
      getItemTitle={item => item.data}
      viewState={{
        'tree-1': {
          expandedItems: ['Fruit', 'Meals'],
        },
      }}
    >
      <Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
    </UncontrolledTreeEnvironment>
  );
}
```

## Configurability

The prop `canSearchByStartingTyping` manages whether the search can be initiated by just starting to type
while focusing the tree. In the following example, the hotkey `F1` needs to be pressed to start search.

```jsx live
function App() {
  return (
    <UncontrolledTreeEnvironment
      dataProvider={new StaticTreeDataProvider(longTree.items, (item, data) => ({ ...item, data }))}
      getItemTitle={item => item.data}
      viewState={{
        'tree-2': {
          expandedItems: ['Fruit', 'Meals'],
        },
      }}
      canSearchByStartingTyping={false}
      keyboardBindings={{
        startSearch: ['f1'],
      }}
    >
      <Tree treeId="tree-2" rootItem="root" treeLabel="Tree Example" />
    </UncontrolledTreeEnvironment>
  );
}
```

Search can also be disabled completely by setting the prop `canSearch` to false.

```jsx live
function App() {
  return (
    <UncontrolledTreeEnvironment
      dataProvider={new StaticTreeDataProvider(longTree.items, (item, data) => ({ ...item, data }))}
      getItemTitle={item => item.data}
      viewState={{
        'tree-3': {
          expandedItems: ['Fruit', 'Meals'],
        },
      }}
      canSearch={false}
    >
      <Tree treeId="tree-3" rootItem="root" treeLabel="Tree Example" />
    </UncontrolledTreeEnvironment>
  );
}
```

## Programmatic interaction

This feature can programmatically be controlled by pulling a React Ref either from the tree environment
or the tree itself, and acting on the Ref object. [Read the documentation on externally interacting
with the tree via Refs](/docs/guides/refs) to find out more.

## Finding items that are not loaded in

The search functionality only searches through items that are currently visible in the tree. This keeps the
functionality in par with similar tree implementations, where you can directly type into a tree to jump to already
available items, giving an easier way to scroll through large trees.

Searching through all items and expanding potentially hidden items to expose the searched item is a bit more complicated,
and depends on your data structure to work. Because there are several ways to define a data structure with RCT,
it is up to you to implement the logic to actually find the item that should be displayed. Once you determined a path
to the item (i.e. an array of item ids), you can use the `expandSubsequently(treeId, path)` function in the
[Tree environment ref](/docs/api/interfaces/TreeEnvironmentRef) to expand the tree to the searched item, or use
the `expandSubsequently(path)` function in the [Tree Ref](/docs/api/interfaces/TreeRef).

Try entering "blackberry" in the example below, and clicking on "Find item":

```jsx live
function App() {
  const [search, setSearch] = useState('');
  const tree = useRef(null)

  const dataProvider = useMemo(
    () =>
      new StaticTreeDataProvider(longTree.items, (item, data) => ({
        ...item,
        data,
      })),
    []
  );

  const findItemPath = useCallback(
    async (search, searchRoot = 'root') => {
      const item = await dataProvider.getTreeItem(searchRoot);
      if (item.data.toLowerCase().includes(search.toLowerCase())) {
        return [item.index];
      }
      const searchedItems = await Promise.all(
        item.children && item.children.map(child => findItemPath(search, child)) || []
      );
      const result = searchedItems.find(item => item !== null);
      if (!result) {
        return null;
      }
      return [item.index, ...result];
    },
    [dataProvider]
  );

  const find = useCallback(
    e => {
      e.preventDefault();
      if (search) {
        findItemPath(search).then(path => {
          if (path) {
            // wait for full path including leaf, to make sure it loaded in
            tree.current?.expandSubsequently(path).then(() => {
                tree.current.selectItems([path[path.length - 1]]);
                tree.current.focusItem(path[path.length - 1]);
              });
          }
        });
      }
    },
    [findItemPath, search]
  );

  return (
    <>
      <form onSubmit={find}>
        <input
          value={search}
          onChange={e => setSearch(e.target.value)}
          placeholder="Search..."
        />
        <button type="submit">Find item</button>
      </form>
      <UncontrolledTreeEnvironment
        dataProvider={dataProvider}
        getItemTitle={item => item.data}
        viewState={{
          'tree-4': {},
        }}
      >
        <Tree
          treeId="tree-4"
          rootItem="root"
          treeLabel="Tree Example"
          ref={tree}
        />
      </UncontrolledTreeEnvironment>
    </>
  );
}
```
